#include "Test.h"

#include <windows.h>
#include <stdio.h>
#include <winbase.h>
//#include <stdlib.h>
//#include <wtypes.h>
//#include <math.h>
//#include <assert.h>
//#include <dbt.h>
//#include "hidsdi.h"
//#include <strsafe.h>
#include <setupapi.h>
#include <ddk/hidsdi.h>

#define INPUT_REPORT_SIZE 128

static char haha[80] = "haha";
static char ungung[80] = "ungung";

HINSTANCE hHID = NULL;
HINSTANCE hSET = NULL;
HANDLE DeviceHandle;
//WINHIDSDI BOOL WINAPI HidD_SetOutputReport(HANDLE, PVOID, ULONG);

JNIEXPORT jlong JNICALL Java_Test_hello
  (JNIEnv *env, jobject o, jstring s, jlong l)
{
	return (0);
}

void ErrorExit(LPTSTR lpszFunction) 
{ 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;

    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
    wsprintf((LPTSTR)lpDisplayBuf, 
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw); 
}


HANDLE lookup_device(JNIEnv *env, jobject o, jstring guid)
{
	//Pointers to a function are used, therefore:
	typedef VOID (__stdcall *PHidD_GetProductString)(HANDLE, PVOID, ULONG);
	typedef VOID (__stdcall *PHidD_GetHidGuid)(LPGUID);
	typedef BOOL (__stdcall *PHidD_GetAttributes)(HANDLE, PHIDD_ATTRIBUTES);
	typedef BOOL (__stdcall *PHidD_SetFeature)(HANDLE, PVOID, ULONG);
	typedef BOOL (__stdcall *PHidD_GetFeature)(HANDLE, PVOID, ULONG);
	typedef HDEVINFO (__stdcall *PSetupDiGetClassDevs)(CONST GUID*, PCSTR, HWND, DWORD);
	typedef BOOL (__stdcall *PSetupDiEnumDeviceInterfaces)(HDEVINFO,PSP_DEVINFO_DATA,CONST GUID*,DWORD,PSP_DEVICE_INTERFACE_DATA);
	typedef BOOL (__stdcall *PSetupDiDestroyDeviceInfoList)(HDEVINFO);
	typedef BOOL (__stdcall *PSetupDiGetDeviceInterfaceDetailW)(HDEVINFO,PSP_DEVICE_INTERFACE_DATA,PSP_DEVICE_INTERFACE_DETAIL_DATA_W,DWORD,PDWORD,PSP_DEVINFO_DATA);
	typedef struct _HIDD_ATTRIBUTES
	{
		ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES)
		USHORT VendorID;
		USHORT ProductID;
		USHORT VersionNumber;
	} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;

	PHidD_GetProductString HidD_GetProductString = NULL;
	PHidD_GetHidGuid HidD_GetHidGuid = NULL;
	PHidD_GetAttributes HidD_GetAttributes = NULL;
	PHidD_SetFeature HidD_SetFeature = NULL;
	PHidD_GetFeature HidD_GetFeature = NULL;
	PSetupDiGetClassDevs SetupDiGetClassDevs = NULL;
	PSetupDiEnumDeviceInterfaces SetupDiEnumDeviceInterfaces = NULL;
	PSetupDiDestroyDeviceInfoList SetupDiDestroyDeviceInfoList = NULL;
	PSetupDiGetDeviceInterfaceDetailW SetupDiGetDeviceInterfaceDetailW = NULL;

	//Load the library:
	hHID = LoadLibrary("HID.DLL");
	hSET = LoadLibrary("SETUPAPI.DLL");

	//Update the pointers:
	HidD_GetProductString = (PHidD_GetProductString)
		GetProcAddress(hHID, "HidD_GetProductString");
	HidD_GetHidGuid = (PHidD_GetHidGuid)
		GetProcAddress(hHID, "HidD_GetHidGuid");
	HidD_GetAttributes = (PHidD_GetAttributes)
		GetProcAddress(hHID, "HidD_GetAttributes");
	HidD_SetFeature = (PHidD_SetFeature)
		GetProcAddress(hHID, "HidD_SetFeature");
	HidD_GetFeature = (PHidD_GetFeature)
		GetProcAddress(hHID, "HidD_GetFeature");
	SetupDiGetClassDevs = (PSetupDiGetClassDevs)
		GetProcAddress(hSET, "SetupDiGetClassDevsA");
	SetupDiEnumDeviceInterfaces = (PSetupDiEnumDeviceInterfaces)
		GetProcAddress(hSET, "SetupDiEnumDeviceInterfaces");
	SetupDiDestroyDeviceInfoList = (PSetupDiDestroyDeviceInfoList)
		GetProcAddress(hSET, "SetupDiDestroyDeviceInfoList");
	SetupDiGetDeviceInterfaceDetailW = (PSetupDiGetDeviceInterfaceDetailW)
		GetProcAddress(hSET, "SetupDiGetDeviceInterfaceDetailW");
//	# = (P#)
//		GetProcAddress(hSET, "#");
	GUID HidGuid;
	(HidD_GetHidGuid)(&HidGuid);
	//printf("HidGuid = %d\n", (int)HidGuid);

	HDEVINFO hDevInfo;
	//HANDLE DeviceHandle;
	hDevInfo = (SetupDiGetClassDevs)(&HidGuid,NULL,NULL,DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);
	if (hDevInfo == INVALID_HANDLE_VALUE) return ((*env)->NewStringUTF(env, ungung));

	//Identify each HID interface
	SP_DEVICE_INTERFACE_DATA devInfoData;

	devInfoData.cbSize = sizeof(devInfoData);
	//printf("cbSize = %d\n", devInfoData.cbSize);
	DWORD MemberIndex = 0;
	int Result;
	while (1)
	{
		Result = SetupDiEnumDeviceInterfaces(hDevInfo,0,&HidGuid,MemberIndex,&devInfoData);
		printf("Result = %d\n", Result);
		if (!Result)
		{
			(SetupDiDestroyDeviceInfoList)(hDevInfo);
			return (0); //No more devices found
		}
		MemberIndex++;

		//Get the Pathname of the current device
		typedef struct MY_SP_DEVICE_INTERFACE_DETAIL_DATA_W {
			DWORD  cbSize;
			WCHAR  DevicePath[100];
		} MYSP_DEVICE_INTERFACE_DETAIL_DATA_W, *MYPSP_DEVICE_INTERFACE_DETAIL_DATA_W;

		//MYSP_DEVICE_INTERFACE_DETAIL_DATA_W detailData;
		SP_DEVICE_INTERFACE_DETAIL_DATA_W detailData;
		//detailData.cbSize = sizeof(detailData);
		detailData.cbSize = 6;
//int i;
//for (i=0; i<20; i++) {
//detailData.cbSize = i;
		printf("cbSize = %d\n", (int)detailData.cbSize);
		DWORD Reguired = 0;
		Result = SetupDiGetDeviceInterfaceDetailW(hDevInfo,&devInfoData,&detailData,256,&Reguired,NULL);
		if (!Result)
			continue;
		printf("Manse!\n");
		printf("Path = %ls\n", detailData.DevicePath);
//}

		//printf("Path = %s\n", detailData.DevicePath);
		//wchar_t path[] = "\\\\??\\hid#bthidjoystk#2&CC7A8D&0&0000";
		//lstrcpyW(detailData.DevicePath, L"\\\\??\\hid#bthidjoystk#2&CC7A8D&0&0000");

		//strcpy(detailData.DevicePath, "\\\\??\\hid#bthidjoystk#2&CC7A8D&0&0000");
		//printf("Path = %s\n", detailData.DevicePath);
		//strcpy(detailData.DevicePath, "\\\\?\\USB#VID_046D&PID_C016#6&34166D6F&0&1");

		//Get Handle for the current device
		//DeviceHandle = CreateFile (detailData.DevicePath,
		//		GENERIC_READ|GENERIC_ARITE,
		//		FILE_SHARE_READ|FILE_SHARE_ARITE,
		//		(LPSECURITY_ATTRIBUTES)NULL,
		//		OPEN_EXISTING,
		//		0,
		//		NULL);
		DeviceHandle = CreateFileW (detailData.DevicePath,
				//GENERIC_READ,
				GENERIC_READ|GENERIC_WRITE,
				FILE_SHARE_READ|FILE_SHARE_WRITE,
				(LPSECURITY_ATTRIBUTES)NULL,
				OPEN_EXISTING,
				0,
				//FILE_FLAG_OVERLAPPED,
				NULL);
		printf("DeviceHandle = %d\n", (int)DeviceHandle);
		if (DeviceHandle == INVALID_HANDLE_VALUE)
		{
			ErrorExit("CreateFile");
			printf("Error code: %d\n", (int)GetLastError());
			CloseHandle(DeviceHandle);
			printf("CloseHandle 1\n");
			continue;
		}

		//Read Attributes from the current device
		HIDD_ATTRIBUTES Attributes;
		Attributes.Size = sizeof(Attributes);

		Result = HidD_GetAttributes(DeviceHandle,&Attributes);
		if (!Result)
		{
			CloseHandle(DeviceHandle);
			printf("CloseHandle 2\n");
			continue;
		}
		// All information obtained
		// Attributes.VendorID
		// Attributes.ProductID
		// detailData.DevicePath <- Remember for future use
		// easy example can be:
		if ((Attributes.VendorID == 0x57e) &&
				(Attributes.ProductID == 0x306)) {
			printf("Obtained Path = %ls\n", detailData.DevicePath);
			printf("VID/PID = %x/%x\n", Attributes.VendorID, Attributes.ProductID);
			//return detailData.DevicePath;
			return DeviceHandle;
		}
		CloseHandle(DeviceHandle);
	} //end of while
	return NULL;
}

int LoopAction(HANDLE wiimotehandle, LPBYTE buffer, BOOL process)
{
	DWORD result;
	PDWORD size = malloc(sizeof(DWORD));
	BOOL nobuffer = FALSE;
	OVERLAPPED *hidoverlap = NULL;

	/* event that can be signaled */
	if(hidoverlap == NULL)
	{
		hidoverlap = malloc(sizeof(OVERLAPPED));
		hidoverlap->hEvent = CreateEvent(NULL, TRUE, TRUE, ""); 
		hidoverlap->Offset = 0;
		hidoverlap->OffsetHigh = 0;
	}

	if(buffer == NULL)
	{
		buffer = malloc(22 * sizeof(BYTE));
		nobuffer = TRUE;
	}

	if(!ReadFile(wiimotehandle, buffer, 22, size, hidoverlap))
	{
		if(GetLastError() == ERROR_IO_PENDING)
		{
			result = WaitForSingleObject(hidoverlap->hEvent, 100);
			if(result == WAIT_TIMEOUT)
			{
				CancelIo(wiimotehandle);
				return 0;
			}
			else if(result == WAIT_FAILED)
			{
				ErrorExit("Read Wait Error");
				return 0;
			}
			GetOverlappedResult(wiimotehandle, hidoverlap, 
					size, FALSE);
		}
		else
		{
			ErrorExit("Read Error\n");
		}
	}
	ResetEvent(hidoverlap->hEvent);
	if(process)
		printf("%x %x %x %x\n", buffer, buffer[1], buffer[2], buffer[3]);
		//ParseReport(buffer);
	if(nobuffer)
		free(buffer);

	free(size);
	return 1;
}

JNIEXPORT jstring JNICALL Java_Test_HidD_1GetHidGuid
  (JNIEnv *env, jobject o, jstring guid)
{
//typedef BOOLEAN (__stdcall *PHidD_GetInputReport (HANDLE, PVOID ULONG  ReportBufferLength
//    );
	HANDLE ReadHandle;
	ReadHandle = lookup_device(env, o, guid);

	if ((ReadHandle == INVALID_HANDLE_VALUE) || (ReadHandle == NULL)) {
			CloseHandle(DeviceHandle);
			ErrorExit("CreateFile");
	}
	LPBYTE buffer[100];
	BOOL process;

/*
int i=0;
while(i++ < 10) {
	LoopAction(ReadHandle, buffer, process);
}
*/

//WINBASEAPI BOOL WINAPI WriteFile(HANDLE,PCVOID,DWORD,PDWORD,LPOVERLAPPED);

	int Result;

	DWORD BytesWrite = 0;
	BYTE report[512];
	BYTE data[2];
	/* set mode to motion + (basic) IR */
	report[0] = 0x12;
	report[1] = 0x00;
	report[2] = 0x33;
	//char report[512] = "";
	DWORD s = sizeof(report);	
	printf("pnt = %p\n", report);
	//Result = WriteFile(ReadHandle, report, sizeof(report), &BytesWrite, NULL);
	printf("%d %d\n", Result, BytesWrite);
	if (!Result)
		ErrorExit("While setting output report.");
	printf("Sent: %x %x\n", report[0], report[1]);

//	BYTE report[3];
//	BYTE data[2];
//	/* set mode to motion + (basic) IR */
//	report[0] = 0x12;
//	report[1] = 0x00;
//	report[2] = 0x33;
//	if(HidD_SetOutputReport(ReadHandle, report, 3) == 0)
//		ErrorExit("While setting output report.");
//	printf("Sent: %x %x\n", report[0], report[1]);
//	/* enable IR (2 parts) */
//	report[0] = 0x13;
//	report[1] = 0x04;
//	if(HidD_SetOutputReport(ReadHandle, report, 2) == 0)
//		ErrorExit("While setting output report.");
//	printf("Sent: %x %x\n", report[0], report[1]); 
//	report[0] = 0x1A;
//	report[1] = 0x04;
//	if(HidD_SetOutputReport(ReadHandle, report, 2) == 0)
//		ErrorExit("While setting output report.");
//	printf("Sent: %x %x\n", report[0], report[1]); 
//	/* marcan's stuff */
//	data[0] = 0x08;
//	WriteData(0x04B00030, data, 1);
//	data[0] = 0x90;
//	WriteData(0x04B00006, data, 1);
//	data[0] = 0xC0;
//	WriteData(0x04B00008, data, 1);
//	data[0] = 0x40;
//	WriteData(0x04B0001A, data, 1);
//	data[0] = 0x33;
//	WriteData(0x04B00033, data, 1);



	DWORD BytesRead = 0;
	char Report[INPUT_REPORT_SIZE] = "hoho";

int i = 0;
while(i++ < 10) {
	memset(&Report, 0, INPUT_REPORT_SIZE);
	Result = ReadFile (ReadHandle,Report,sizeof(Report),&BytesRead,NULL);
	//Result = HidD_GetInputReport(ReadHandle, &Report, INPUT_REPORT_SIZE);
	//int i;
	if (!Result) {
		ErrorExit("ReadFile");
		return NULL;
	}
	int j = 0;
	for (j = 0; j < 15; j++) {
		printf("%x\t", Report[j]);
	}
	printf("\n");
}

	return ((*env)->NewStringUTF(env, haha));
}

